package cn.xorange.commons.security.service;


import cn.xorange.commons.security.annotation.Authorize;
import cn.xorange.commons.security.constants.SecurityConstants;
import cn.xorange.commons.security.context.SecurityContextHolder;
import cn.xorange.commons.security.entity.SecurityUser;
import cn.xorange.commons.security.exception.SecurityErrorCode;
import cn.xorange.commons.utils.http.ServletUtils;
import cn.xorange.commons.utils.lang.StringUtils;
import org.springframework.web.method.HandlerMethod;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.Set;


public class PermissionService {

    @Resource
    SecurityService securityService;

    private static final String DELIMETER = ",";

    public boolean verifyAuth(HttpServletResponse response, HandlerMethod clazz, Method method){

        // 校验权限
        if (method.isAnnotationPresent(Authorize.class)) {

            Authorize clazzAuth = clazz.getBeanType().getAnnotation(Authorize.class);
            String clazzPerm = "", lackPrem = "";
            if (Objects.nonNull(clazzAuth)) {
                clazzPerm = clazzAuth.hasPerm();
                lackPrem = clazzAuth.lacksPerm();
            }
            Authorize methodAuth = method.getAnnotation(Authorize.class);
            String[] menuPerms = null;
            String[] lacksPerms = null;
            String[] menuRoles = null;
            String[] lacksRoles = null;
            if (!methodAuth.hasPerm().isEmpty()) {
                menuPerms = methodAuth.hasPerm().split(DELIMETER);
                for (int i = 0; i < menuPerms.length; i++) {
                    if (!clazzPerm.isEmpty() && menuPerms[i].split(":").length == 1) {
                        menuPerms[i] = StringUtils.format("{}:{}", clazzPerm, menuPerms[i]);
                    }
                }
                SecurityContextHolder.setContext(Arrays.toString(menuPerms));
            }

            if (!methodAuth.lacksPerm().isEmpty()) {
                lacksPerms = methodAuth.lacksPerm().split(DELIMETER);
                for (int i = 0; i < lacksPerms.length; i++) {
                    if (!lackPrem.isEmpty() && lacksPerms[i].split(":").length == 1) {
                        lacksPerms[i] = StringUtils.format("{}:{}", lackPrem, lacksPerms[i]);
                    }
                }
            }


            if (!methodAuth.hasRole().isEmpty()) {
                menuRoles = methodAuth.hasRole().split(DELIMETER);
            }
            if (!methodAuth.lacksRole().isEmpty()) {
                lacksRoles = methodAuth.lacksRole().split(DELIMETER);
            }

            if (Objects.nonNull(menuPerms) || Objects.nonNull(lacksPerms) || Objects.nonNull(menuRoles) || Objects.nonNull(lacksRoles)) {
                SecurityUser user = securityService.getLoginUser();
                if(user.getPermissions().contains(SecurityConstants.ADMIN_PERMISSION)){
                    return true;
                }
                if(user.getRoleKeys().contains(SecurityConstants.ADMIN_ROLE)){
                    return true;
                }

                boolean boolMenuPerm = false, boolLacksPerm = false, boolMenuRole = false, boolLacksRole = false;
                StringBuilder sb = new StringBuilder();
                if (Objects.nonNull(menuPerms)) {
                    boolMenuPerm = this.hasAnyPermissions(user.getPermissions(), menuPerms);
                    if(!boolMenuPerm){
                        sb.append(StringUtils.format("权限需{}", String.join(DELIMETER,menuPerms)) );
                    }
                }
                if (Objects.nonNull(lacksPerms)) {
                    boolLacksPerm = this.lacksAnyPermissions(user.getPermissions(), lacksPerms);
                    if(!boolLacksPerm){
                        sb.append(StringUtils.format("权限不含{}", String.join(DELIMETER,lacksPerms)) );
                    }
                }
                if (Objects.nonNull(menuRoles)) {
                    boolMenuRole = this.hasAnyRoles(user.getRoleKeys(), menuRoles);
                    if(!boolMenuRole){
                        sb.append(StringUtils.format("角色需{}", String.join(DELIMETER,menuRoles)) );
                    }
                }
                if (Objects.nonNull(lacksRoles)) {
                    boolLacksRole = this.lacksAnyRoles(user.getRoleKeys(), lacksRoles);
                    if(!boolLacksRole){
                        sb.append(StringUtils.format("角色不含{}", String.join(DELIMETER,lacksRoles)) );
                    }
                }
                if (boolMenuPerm || boolLacksPerm || boolMenuRole || boolLacksRole) {
                    return true;
                } else {
                    SecurityErrorCode code = SecurityErrorCode.AUTH_TOKEN_FORBIDDEN;
                    ServletUtils.responseOut(response,code.getStatus(),code.getCode(),sb.toString(),null);
                }

            }
        }

        return true;
    }

    private boolean hasAnyPermissions(Set<String> permissions, String... menuPermission) {
        if(permissions.isEmpty()){
            return false;
        }

        for(String mp : menuPermission) {
            if(permissions.contains(mp)){
                return true;
            }
        }
        return false;
    }

    private boolean lacksAnyPermissions(Set<String> permissions, String... menuPermissions) {
        if(permissions.isEmpty()){
            return true;
        }
        for(String mp : menuPermissions) {
            if(permissions.contains(mp)){
                return false;
            }
        }
        return true;
    }

    private boolean hasAnyRoles(Set<String> roles, String... menuRoles) {
        if(roles.isEmpty()){
            return false;
        }
        for(String r : menuRoles) {
            if(roles.contains(r)){
                return true;
            }
        }
        return false;
    }
    private boolean lacksAnyRoles(Set<String> roles, String... menuRoles) {
        if(roles.isEmpty()){
            return true;
        }
        for(String r : menuRoles) {
            if(roles.contains(r)){
                return false;
            }
        }
        return true;
    }
}
